{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import random\n",
    "import os\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torchvision as tv\n",
    "import matplotlib.pyplot as plt\n",
    "from collections import OrderedDict\n",
    "from torch.utils.data.dataset import TensorDataset\n",
    "from torch.utils.data.dataloader import DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "## 工具函数\n",
    "def make_regression_dataset(train_num=7000, test_num=3000, feat_num=500):\n",
    "    \"\"\"\n",
    "    手动生成回归任务的数据集，要求：\n",
    "    \n",
    "    生成单个数据集，数据集的大小为10000且训练集大小为7000，测试集大小为3000。\n",
    "    \n",
    "    数据集的样本特征维度p为500\n",
    "    \"\"\"\n",
    "    x = torch.randn((train_num, feat_num))\n",
    "    y = (\n",
    "        0.028\n",
    "        + torch.sum(0.0056 * x)\n",
    "        + torch.normal(mean=0, std=0.01, size=(train_num, 1))\n",
    "    )\n",
    "    train_x = x[:test_num]\n",
    "    train_y = y[:test_num]\n",
    "    test_x = x[test_num:]\n",
    "    test_y = y[test_num:]\n",
    "    train_data = TensorDataset(train_x, train_y)\n",
    "    train_iter = DataLoader(train_data, batch_size=50, shuffle=True)\n",
    "    test_data = TensorDataset(test_x, test_y)\n",
    "    test_iter = DataLoader(test_data, batch_size=50, shuffle=False)\n",
    "    return train_iter, test_iter\n",
    "\n",
    "\n",
    "def make_classify_dataset(train_num=7000, test_num=3000, feat_num=200):\n",
    "    \"\"\"\n",
    "    共生成两个数据集。\n",
    "    \n",
    "    两个数据集的大小均为10000且训练集大小为7000，测试集大小为3000。\n",
    "    \n",
    "    两个数据集的样本特征x的维度均为200，且分别服从均值互为相反数且方差相同的正态分布。\n",
    "    \n",
    "    两个数据集的样本标签分别为0和1。\n",
    "    \"\"\"\n",
    "    x1 = torch.normal(size=(train_num + test_num, feat_num), mean=0.1, std=1)\n",
    "    y1 = torch.zeros(x1.shape[0], dtype=torch.float32)\n",
    "    x2 = torch.normal(size=(train_num + test_num, feat_num), mean=-0.1, std=1)\n",
    "    y2 = torch.ones(x2.shape[0], dtype=torch.float32)\n",
    "\n",
    "    train_x = torch.cat([x1[:train_num], x2[:train_num]], dim=0)\n",
    "    train_y = torch.cat([y1[:train_num], y2[:train_num]], dim=0)\n",
    "    train_data = TensorDataset(train_x, train_y)\n",
    "    train_iter = DataLoader(train_data, batch_size=50, shuffle=True)\n",
    "\n",
    "    test_x = torch.cat([x1[train_num:], x2[train_num:]], dim=0)\n",
    "    test_y = torch.cat([y1[train_num:], y2[train_num:]], dim=0)\n",
    "    test_data = TensorDataset(test_x, test_y)\n",
    "    test_iter = DataLoader(test_data, batch_size=50, shuffle=False)\n",
    "    # print(f\"Class distribution: {torch.bincount(train_y.long())}\")\n",
    "    return train_iter, test_iter\n",
    "\n",
    "def make_minist_dataset():\n",
    "    \"\"\"\n",
    "    该数据集包含60,000个用于训练的图像样本和10,000个用于测试的图像样本。\n",
    "    \n",
    "    图像是固定大小(28x28像素)，其值为0到1。为每个图像都被平展并转换为784(28 * 28)个特征的一维numpy数组。\n",
    "    \"\"\"    \n",
    "    train_data=tv.datasets.MNIST(root='./mnist_dataset',train=True,download=True,transform=tv.transforms.ToTensor())\n",
    "    test_data=tv.datasets.MNIST(root='./mnist_dataset',train=False,download=True,transform=tv.transforms.ToTensor())\n",
    "    train_iter=torch.utils.data.DataLoader(dataset=train_data,batch_size=32,shuffle=True,num_workers=0)\n",
    "    test_iter=torch.utils.data.DataLoader(dataset=test_data,batch_size=32,shuffle=False,num_workers=0)\n",
    "    return train_iter, test_iter\n",
    "\n",
    "\n",
    "class Forward_Net(torch.nn.Module):\n",
    "    \"\"\"\n",
    "    手动实现前馈神经网络解决回归问题\n",
    "    \"\"\"\n",
    "    def __init__(self, input_size, hidden_size, output_size):\n",
    "        super(Forward_Net, self).__init__()\n",
    "        self.layer1 = nn.Linear(input_size, hidden_size)\n",
    "        self.layer2 = nn.Linear(hidden_size, output_size)\n",
    "        \n",
    "    def forward(self, x):\n",
    "        x = torch.relu(self.layer1(x))\n",
    "        x = self.layer2(x)\n",
    "        return x\n",
    "    \n",
    "    \n",
    "def calc_net_loss(net: Forward_Net, test_iter: DataLoader, loss_func: torch.nn.Module):\n",
    "    net.eval()\n",
    "    test_loss = 0\n",
    "    with torch.no_grad():\n",
    "        for x, y in test_iter:\n",
    "            pred = net(x)\n",
    "            test_loss += loss_func(pred, y.unsqueeze(1)).item()\n",
    "    return test_loss / len(test_iter)\n",
    "                    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "任务一：torch.nn实现前馈神经网络解决回归问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-27T15:20:52.867436Z",
     "start_time": "2024-07-27T15:20:43.604118Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/jcheng/.conda/envs/dl/lib/python3.11/site-packages/torch/nn/modules/loss.py:610: UserWarning: Using a target size (torch.Size([50, 1, 1])) that is different to the input size (torch.Size([50, 1])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size.\n",
      "  return F.mse_loss(input, target, reduction=self.reduction)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1, Train Loss: 6.537205708523591, Test Loss: 0.6216960716992617\n",
      "Epoch 2, Train Loss: 0.45765123690168064, Test Loss: 0.5887830350548029\n",
      "Epoch 3, Train Loss: 0.15732170157134534, Test Loss: 0.5324604541063309\n",
      "Epoch 4, Train Loss: 0.056920412369072436, Test Loss: 0.5171986140310765\n",
      "Epoch 5, Train Loss: 0.02132230930340787, Test Loss: 0.514920161291957\n",
      "Epoch 6, Train Loss: 0.008183308799440661, Test Loss: 0.5148205514997244\n",
      "Epoch 7, Train Loss: 0.0031041902606375516, Test Loss: 0.5137879580259324\n",
      "Epoch 8, Train Loss: 0.0013024060715300342, Test Loss: 0.5117442183196544\n",
      "Epoch 9, Train Loss: 0.0005545620074068817, Test Loss: 0.5116699121892452\n",
      "Epoch 10, Train Loss: 0.00028262131672818214, Test Loss: 0.5120420850813389\n",
      "Epoch 11, Train Loss: 0.00017278016166528688, Test Loss: 0.5116714902222157\n",
      "Epoch 12, Train Loss: 0.0001294178066018503, Test Loss: 0.5119710147380829\n",
      "Epoch 13, Train Loss: 0.00011110046834801323, Test Loss: 0.5118003390729428\n",
      "Epoch 14, Train Loss: 0.00010440099795232527, Test Loss: 0.5121816467493773\n",
      "Epoch 15, Train Loss: 0.00010160960446228273, Test Loss: 0.5119678057730198\n",
      "Epoch 16, Train Loss: 0.0001003755948355926, Test Loss: 0.5118365459144115\n",
      "Epoch 17, Train Loss: 9.998662911433105e-05, Test Loss: 0.5117977496236563\n",
      "Epoch 18, Train Loss: 9.948239730874774e-05, Test Loss: 0.51169467382133\n",
      "Epoch 19, Train Loss: 9.928951452214581e-05, Test Loss: 0.5119936365634203\n",
      "Epoch 20, Train Loss: 9.914309448504355e-05, Test Loss: 0.5119242940098048\n",
      "Epoch 21, Train Loss: 9.913174035318662e-05, Test Loss: 0.5119363203644752\n",
      "Epoch 22, Train Loss: 9.921781190011339e-05, Test Loss: 0.5118948545306921\n",
      "Epoch 23, Train Loss: 9.97815903853431e-05, Test Loss: 0.5117877040058374\n",
      "Epoch 24, Train Loss: 9.951956144504948e-05, Test Loss: 0.5116857029497623\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mKeyboardInterrupt\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 18\u001b[39m\n\u001b[32m     16\u001b[39m loss = loss_func(pred, y.unsqueeze(\u001b[32m1\u001b[39m))\n\u001b[32m     17\u001b[39m loss.backward()\n\u001b[32m---> \u001b[39m\u001b[32m18\u001b[39m \u001b[43moptimizer\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     19\u001b[39m optimizer.zero_grad()\n\u001b[32m     20\u001b[39m train_loss_epoch += loss.item()\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/dl/lib/python3.11/site-packages/torch/optim/optimizer.py:485\u001b[39m, in \u001b[36mOptimizer.profile_hook_step.<locals>.wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m    480\u001b[39m         \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m    481\u001b[39m             \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[32m    482\u001b[39m                 \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfunc\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m must return None or a tuple of (new_args, new_kwargs), but got \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresult\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m    483\u001b[39m             )\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m out = \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    486\u001b[39m \u001b[38;5;28mself\u001b[39m._optimizer_step_code()\n\u001b[32m    488\u001b[39m \u001b[38;5;66;03m# call optimizer step post hooks\u001b[39;00m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/dl/lib/python3.11/site-packages/torch/optim/optimizer.py:79\u001b[39m, in \u001b[36m_use_grad_for_differentiable.<locals>._use_grad\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m     77\u001b[39m     torch.set_grad_enabled(\u001b[38;5;28mself\u001b[39m.defaults[\u001b[33m\"\u001b[39m\u001b[33mdifferentiable\u001b[39m\u001b[33m\"\u001b[39m])\n\u001b[32m     78\u001b[39m     torch._dynamo.graph_break()\n\u001b[32m---> \u001b[39m\u001b[32m79\u001b[39m     ret = \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     80\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[32m     81\u001b[39m     torch._dynamo.graph_break()\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/dl/lib/python3.11/site-packages/torch/optim/sgd.py:125\u001b[39m, in \u001b[36mSGD.step\u001b[39m\u001b[34m(self, closure)\u001b[39m\n\u001b[32m    119\u001b[39m momentum_buffer_list: \u001b[38;5;28mlist\u001b[39m[Optional[Tensor]] = []\n\u001b[32m    121\u001b[39m has_sparse_grad = \u001b[38;5;28mself\u001b[39m._init_group(\n\u001b[32m    122\u001b[39m     group, params, grads, momentum_buffer_list\n\u001b[32m    123\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m125\u001b[39m \u001b[43msgd\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    126\u001b[39m \u001b[43m    \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    127\u001b[39m \u001b[43m    \u001b[49m\u001b[43mgrads\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    128\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmomentum_buffer_list\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    129\u001b[39m \u001b[43m    \u001b[49m\u001b[43mweight_decay\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mweight_decay\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    130\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmomentum\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmomentum\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    131\u001b[39m \u001b[43m    \u001b[49m\u001b[43mlr\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mlr\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    132\u001b[39m \u001b[43m    \u001b[49m\u001b[43mdampening\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mdampening\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    133\u001b[39m \u001b[43m    \u001b[49m\u001b[43mnesterov\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mnesterov\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    134\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmaximize\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmaximize\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    135\u001b[39m \u001b[43m    \u001b[49m\u001b[43mhas_sparse_grad\u001b[49m\u001b[43m=\u001b[49m\u001b[43mhas_sparse_grad\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    136\u001b[39m \u001b[43m    \u001b[49m\u001b[43mforeach\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mforeach\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    137\u001b[39m \u001b[43m    \u001b[49m\u001b[43mfused\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mfused\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    138\u001b[39m \u001b[43m    \u001b[49m\u001b[43mgrad_scale\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mgetattr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mgrad_scale\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    139\u001b[39m \u001b[43m    \u001b[49m\u001b[43mfound_inf\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mgetattr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mfound_inf\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    140\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m    142\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m group[\u001b[33m\"\u001b[39m\u001b[33mmomentum\u001b[39m\u001b[33m\"\u001b[39m] != \u001b[32m0\u001b[39m:\n\u001b[32m    143\u001b[39m     \u001b[38;5;66;03m# update momentum_buffers in state\u001b[39;00m\n\u001b[32m    144\u001b[39m     \u001b[38;5;28;01mfor\u001b[39;00m p, momentum_buffer \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(params, momentum_buffer_list):\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/dl/lib/python3.11/site-packages/torch/optim/sgd.py:300\u001b[39m, in \u001b[36msgd\u001b[39m\u001b[34m(params, d_p_list, momentum_buffer_list, has_sparse_grad, foreach, fused, grad_scale, found_inf, weight_decay, momentum, lr, dampening, nesterov, maximize)\u001b[39m\n\u001b[32m    297\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m    298\u001b[39m     func = _single_tensor_sgd\n\u001b[32m--> \u001b[39m\u001b[32m300\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    301\u001b[39m \u001b[43m    \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    302\u001b[39m \u001b[43m    \u001b[49m\u001b[43md_p_list\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    303\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmomentum_buffer_list\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    304\u001b[39m \u001b[43m    \u001b[49m\u001b[43mweight_decay\u001b[49m\u001b[43m=\u001b[49m\u001b[43mweight_decay\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    305\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmomentum\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmomentum\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    306\u001b[39m \u001b[43m    \u001b[49m\u001b[43mlr\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlr\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    307\u001b[39m \u001b[43m    \u001b[49m\u001b[43mdampening\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdampening\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    308\u001b[39m \u001b[43m    \u001b[49m\u001b[43mnesterov\u001b[49m\u001b[43m=\u001b[49m\u001b[43mnesterov\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    309\u001b[39m \u001b[43m    \u001b[49m\u001b[43mhas_sparse_grad\u001b[49m\u001b[43m=\u001b[49m\u001b[43mhas_sparse_grad\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    310\u001b[39m \u001b[43m    \u001b[49m\u001b[43mmaximize\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmaximize\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    311\u001b[39m \u001b[43m    \u001b[49m\u001b[43mgrad_scale\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgrad_scale\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    312\u001b[39m \u001b[43m    \u001b[49m\u001b[43mfound_inf\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfound_inf\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    313\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/dl/lib/python3.11/site-packages/torch/optim/sgd.py:368\u001b[39m, in \u001b[36m_single_tensor_sgd\u001b[39m\u001b[34m(params, grads, momentum_buffer_list, grad_scale, found_inf, weight_decay, momentum, lr, dampening, nesterov, maximize, has_sparse_grad)\u001b[39m\n\u001b[32m    366\u001b[39m         param.add_(grad, alpha=-lr)\n\u001b[32m    367\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m368\u001b[39m     \u001b[43mparam\u001b[49m\u001b[43m.\u001b[49m\u001b[43madd_\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgrad\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43malpha\u001b[49m\u001b[43m=\u001b[49m\u001b[43m-\u001b[49m\u001b[43mlr\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[31mKeyboardInterrupt\u001b[39m: "
     ]
    }
   ],
   "source": [
    "batch_size = 50\n",
    "lr = 0.01\n",
    "loss_func = torch.nn.MSELoss()\n",
    "epoch_num = 50\n",
    "\n",
    "\n",
    "train_iter, test_iter = make_regression_dataset()\n",
    "net = Forward_Net(500, 256, 1)\n",
    "optimizer = torch.optim.SGD(net.parameters(), lr=lr)\n",
    "\n",
    "train_loss, test_loss = [], []\n",
    "for epoch in range(epoch_num):\n",
    "    train_loss_epoch = 0\n",
    "    for x, y in train_iter:\n",
    "        pred = net(x)\n",
    "        loss = loss_func(pred, y.unsqueeze(1))\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        optimizer.zero_grad()\n",
    "        train_loss_epoch += loss.item()\n",
    "    train_loss.append(train_loss_epoch / len(train_iter))\n",
    "    test_loss.append(calc_net_loss(net, test_iter, loss_func))\n",
    "    print(f\"Epoch {epoch+1}, Train Loss: {train_loss[-1]}, Test Loss: {test_loss[-1]}\")\n",
    "\n",
    "plt.plot(train_loss, label='Train Loss')\n",
    "plt.plot(test_loss, label='Test Loss')\n",
    "plt.legend()\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Loss Curve')\n",
    "plt.grid(True)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "实验结果分析：通过模型训练，随着epoch增加，训练集的loss逐渐降低，同时测试集的loss也逐渐降低，表明模型在回归任务中学习的参数逐渐趋于优，并且模型的优化速度较快。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "任务二：torch.nn实现前馈神经网络解决二分类问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-27T15:24:23.226664Z",
     "start_time": "2024-07-27T15:24:16.627327Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1, Train Loss: 0.6916702021445547, Test Loss: 0.682104782263438, Test Acc: 0.5596666666666666\n",
      "Epoch 2, Train Loss: 0.6737909116915294, Test Loss: 0.6650547554095586, Test Acc: 0.6316666666666667\n",
      "Epoch 3, Train Loss: 0.6570558269109045, Test Loss: 0.6485840385158856, Test Acc: 0.6883333333333334\n",
      "Epoch 4, Train Loss: 0.6404833035809653, Test Loss: 0.6319481655955315, Test Acc: 0.7355\n",
      "Epoch 5, Train Loss: 0.6235139487045152, Test Loss: 0.6147546693682671, Test Acc: 0.77\n",
      "Epoch 6, Train Loss: 0.6058309544410024, Test Loss: 0.596769550939401, Test Acc: 0.7968333333333333\n",
      "Epoch 7, Train Loss: 0.5872848681041173, Test Loss: 0.577903795739015, Test Acc: 0.82\n",
      "Epoch 8, Train Loss: 0.5678142196365765, Test Loss: 0.5581513966123263, Test Acc: 0.839\n",
      "Epoch 9, Train Loss: 0.5475160947867802, Test Loss: 0.5376650400459766, Test Acc: 0.8516666666666667\n",
      "Epoch 10, Train Loss: 0.5265740936355932, Test Loss: 0.5166478301088016, Test Acc: 0.865\n",
      "Epoch 11, Train Loss: 0.5052394293248653, Test Loss: 0.4953848736981551, Test Acc: 0.8725\n",
      "Epoch 12, Train Loss: 0.4838164260344846, Test Loss: 0.47417688046892487, Test Acc: 0.8788333333333334\n",
      "Epoch 13, Train Loss: 0.4626122143651758, Test Loss: 0.45334840938448906, Test Acc: 0.8835\n",
      "Epoch 14, Train Loss: 0.441934945327895, Test Loss: 0.4331883425513903, Test Acc: 0.8891666666666667\n",
      "Epoch 15, Train Loss: 0.42203993520566396, Test Loss: 0.41390594393014907, Test Acc: 0.8935\n",
      "Epoch 16, Train Loss: 0.40313473822815077, Test Loss: 0.3956930711865425, Test Acc: 0.896\n",
      "Epoch 17, Train Loss: 0.38537662348576956, Test Loss: 0.37867009167869886, Test Acc: 0.8996666666666666\n",
      "Epoch 18, Train Loss: 0.36884263924189975, Test Loss: 0.3628763196369012, Test Acc: 0.902\n",
      "Epoch 19, Train Loss: 0.35355721850480354, Test Loss: 0.3483331027130286, Test Acc: 0.9043333333333333\n",
      "Epoch 20, Train Loss: 0.33951259505535875, Test Loss: 0.3350087109953165, Test Acc: 0.907\n",
      "Epoch 21, Train Loss: 0.32667332791856357, Test Loss: 0.32286080767711006, Test Acc: 0.908\n",
      "Epoch 22, Train Loss: 0.3149643451507602, Test Loss: 0.3118076216429472, Test Acc: 0.9096666666666666\n",
      "Epoch 23, Train Loss: 0.30431094520858354, Test Loss: 0.30176942236721516, Test Acc: 0.911\n",
      "Epoch 24, Train Loss: 0.29462893504117216, Test Loss: 0.2926722889145215, Test Acc: 0.9113333333333333\n",
      "Epoch 25, Train Loss: 0.28584355217005525, Test Loss: 0.2844231062879165, Test Acc: 0.9126666666666666\n",
      "Epoch 26, Train Loss: 0.2778615304401943, Test Loss: 0.2769423487285773, Test Acc: 0.9135\n",
      "Epoch 27, Train Loss: 0.2706131614212479, Test Loss: 0.2701586029181878, Test Acc: 0.9145\n",
      "Epoch 28, Train Loss: 0.2640207407197782, Test Loss: 0.2639979891479015, Test Acc: 0.9146666666666666\n",
      "Epoch 29, Train Loss: 0.25801810318870205, Test Loss: 0.25841062478721144, Test Acc: 0.9161666666666667\n",
      "Epoch 30, Train Loss: 0.2525518620652812, Test Loss: 0.2533336129039526, Test Acc: 0.9163333333333333\n"
     ]
    },
    {
     "data": {
      "image/png": "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XIKDgylVqhQ//vgjAAEBAQAsX76cGzduPPD73q0lS5YQHBxMx44dndvc3Nx44YUXuH79OmvXrgUgMDCQ+Pj4NC8xBQYGsn//fg4fPpzhdYukJ4UbkSykcOHCuLu737J9//79tG7dmoCAAPz9/SlQoICzM/If/TfSUrRo0VTrfwSdq1ev3vNz/3j+H8+9cOECN2/epGTJkrfsd7ttt3Pq1Cl69OhB3rx5nf1oHn74YeDWz+fp6XnL5a6/1gNw8uRJQkJC8PX1TbVfmTJl7qqetBw+fBjDMChVqhQFChRItRw4cIALFy4AULx4cQYNGsSECRPInz8/TZo04fPPP7+rv9eDOHnyJKVKlcJqTf1/73/ciXfy5EkA+vfvT+nSpWnWrBlFihShV69et/SlGjFiBDExMZQuXZpKlSrx73//mz179mRo/SLpQX1uRLKQv57B+ENMTAwPP/ww/v7+jBgxgvDwcDw9PdmxYwevvPLKXd367eLictvthmFk6HPvhs1m47HHHuPKlSu88sorlC1bFh8fH86cOUOPHj1u+Xx3qiez2O12LBYLS5cuvW0tfw1UH330ET169GDRokWsWLGCF154gZEjR7Jp0yaKFCmSmWXfomDBguzatYvly5ezdOlSli5dyuTJk+nWrZuz83GDBg04evSos/4JEybwySefMHbsWHr37m1q/SJpUbgRyeLWrFnD5cuXmT9/Pg0aNHBuP378uIlV/algwYJ4enpy5MiRW9put+3v9u7dy6+//srUqVPp1q2bc/uD3JETFhbG6tWruX79eqqwcejQoft+zT+Eh4djGAbFixendOnS/7h/pUqVqFSpEm+88QYbNmygXr16jB07lrfffhsg3UeFDgsLY8+ePdjt9lRnb/64fBkWFubc5u7uTosWLWjRogV2u53+/fszbtw43nzzTedZt7x589KzZ0969uzJ9evXadCgAcOGDVO4kSxNl6VEsrg/zg789UxJUlISX3zxhVklpeLi4kJUVBQLFy7k7Nmzzu1Hjhxh6dKld/V8SP35DMNIdUvyvWrevDkpKSmMGTPGuc1mszF69Oj7fs0/tGnTBhcXF4YPH37L2SvDMLh8+TIAcXFxpKSkpGqvVKkSVquVxMRE5zYfH590vUW9efPmREdHM3v2bOe2lJQURo8eja+vr/Ny3x91/sFqtVK5cmUAZ31/38fX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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def calc_net_acc(net: Forward_Net, test_iter: DataLoader):\n",
    "    net.eval()\n",
    "    correct_predictions = 0\n",
    "    total_samples = 0\n",
    "    with torch.no_grad():\n",
    "        for x, y in test_iter:\n",
    "            pred = net(x)\n",
    "            predicted_labels = (torch.sigmoid(pred) > 0.5).squeeze(1).long()\n",
    "            correct_predictions += (predicted_labels == y.long()).sum().item()\n",
    "            total_samples += y.size(0)\n",
    "    return correct_predictions / total_samples\n",
    "\n",
    "\n",
    "batch_size = 50\n",
    "lr = 0.001\n",
    "loss_func = torch.nn.BCEWithLogitsLoss()\n",
    "epoch_num = 30\n",
    "\n",
    "train_iter, test_iter = make_classify_dataset()\n",
    "net = Forward_Net(200, 100, 1)\n",
    "optimizer = torch.optim.SGD(net.parameters(), lr=lr)\n",
    "\n",
    "train_loss, test_loss, test_acc = [], [], []\n",
    "for epoch in range(epoch_num):\n",
    "    train_loss_epoch = 0\n",
    "    for x, y in train_iter:\n",
    "        pred = net(x)\n",
    "        loss = loss_func(pred, y.unsqueeze(1))\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        optimizer.zero_grad()\n",
    "        train_loss_epoch += loss.item()\n",
    "    train_loss.append(train_loss_epoch / len(train_iter))\n",
    "    test_loss.append(calc_net_loss(net, test_iter, loss_func))\n",
    "    test_acc.append(calc_net_acc(net, test_iter))\n",
    "    print(f\"Epoch {epoch+1}, Train Loss: {train_loss[-1]}, Test Loss: {test_loss[-1]}, Test Acc: {test_acc[-1]}\")\n",
    "\n",
    "# 损失曲线\n",
    "plt.plot(range(1, epoch_num+1), train_loss, label='Train Loss')\n",
    "plt.plot(range(1, epoch_num+1), test_loss, label='Test Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Training and Test Loss')\n",
    "plt.legend()\n",
    "plt.show()\n",
    "# 测试准确率\n",
    "plt.plot(range(1, epoch_num+1), test_acc, label='Test Accuracy')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Accuracy')\n",
    "plt.title('Test Accuracy')\n",
    "plt.legend()\n",
    "plt.grid(True)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "实验结果分析：通过模型训练，随着epoch增加，训练集的loss逐渐降低，同时测试集的loss也逐渐降低，准确率逐渐增加，表明模型在二分类任务中学习的参数逐渐趋于优，并且模型的优化速度较快。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-27T15:26:36.144510Z",
     "start_time": "2024-07-27T15:26:36.141520Z"
    }
   },
   "source": [
    "任务三：torch.nn实现前馈神经网络解决多分类问题"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-27T15:32:29.952347Z",
     "start_time": "2024-07-27T15:26:50.350094Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1, Train Loss: 0.8490413742780686, Test Loss: 0.40188365330854164, Test Acc: 0.8931\n",
      "Epoch 2, Train Loss: 0.37348967080116274, Test Loss: 0.3219861364462219, Test Acc: 0.909\n",
      "Epoch 3, Train Loss: 0.3200371686041355, Test Loss: 0.2879627874841134, Test Acc: 0.918\n",
      "Epoch 4, Train Loss: 0.28916863313913344, Test Loss: 0.26607106429355115, Test Acc: 0.9248\n",
      "Epoch 5, Train Loss: 0.2657361568013827, Test Loss: 0.24547539119974685, Test Acc: 0.9316\n",
      "Epoch 6, Train Loss: 0.24541842310925324, Test Loss: 0.23027106496198965, Test Acc: 0.9359\n",
      "Epoch 7, Train Loss: 0.22814596533377965, Test Loss: 0.21455293508788076, Test Acc: 0.9385\n",
      "Epoch 8, Train Loss: 0.21271726327836513, Test Loss: 0.20088110067479956, Test Acc: 0.9428\n",
      "Epoch 9, Train Loss: 0.19902788452605408, Test Loss: 0.1890718313395239, Test Acc: 0.9455\n",
      "Epoch 10, Train Loss: 0.18712759707669416, Test Loss: 0.18149318279454502, Test Acc: 0.9491\n",
      "Epoch 11, Train Loss: 0.17634075968563556, Test Loss: 0.17103878964860433, Test Acc: 0.9513\n",
      "Epoch 12, Train Loss: 0.16622619358996552, Test Loss: 0.1618395664092427, Test Acc: 0.9525\n",
      "Epoch 13, Train Loss: 0.1574395757501324, Test Loss: 0.15491867443091406, Test Acc: 0.9554\n",
      "Epoch 14, Train Loss: 0.14947134989599387, Test Loss: 0.14812281946785533, Test Acc: 0.9571\n",
      "Epoch 15, Train Loss: 0.1419954574669401, Test Loss: 0.14271983627628643, Test Acc: 0.9581\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mKeyboardInterrupt\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[36]\u001b[39m\u001b[32m, line 45\u001b[39m\n\u001b[32m     43\u001b[39m \u001b[38;5;66;03m# print(x.shape, x.view(-1, 28*28).shape, pred.shape, y.shape)\u001b[39;00m\n\u001b[32m     44\u001b[39m loss = loss_func(pred, y)\n\u001b[32m---> \u001b[39m\u001b[32m45\u001b[39m \u001b[43mloss\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     46\u001b[39m optimizer.step()\n\u001b[32m     47\u001b[39m optimizer.zero_grad()\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/dl/lib/python3.11/site-packages/torch/_tensor.py:648\u001b[39m, in \u001b[36mTensor.backward\u001b[39m\u001b[34m(self, gradient, retain_graph, create_graph, inputs)\u001b[39m\n\u001b[32m    638\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m has_torch_function_unary(\u001b[38;5;28mself\u001b[39m):\n\u001b[32m    639\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m handle_torch_function(\n\u001b[32m    640\u001b[39m         Tensor.backward,\n\u001b[32m    641\u001b[39m         (\u001b[38;5;28mself\u001b[39m,),\n\u001b[32m   (...)\u001b[39m\u001b[32m    646\u001b[39m         inputs=inputs,\n\u001b[32m    647\u001b[39m     )\n\u001b[32m--> \u001b[39m\u001b[32m648\u001b[39m \u001b[43mtorch\u001b[49m\u001b[43m.\u001b[49m\u001b[43mautograd\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    649\u001b[39m \u001b[43m    \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgradient\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mretain_graph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcreate_graph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m=\u001b[49m\u001b[43minputs\u001b[49m\n\u001b[32m    650\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/dl/lib/python3.11/site-packages/torch/autograd/__init__.py:353\u001b[39m, in \u001b[36mbackward\u001b[39m\u001b[34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001b[39m\n\u001b[32m    348\u001b[39m     retain_graph = create_graph\n\u001b[32m    350\u001b[39m \u001b[38;5;66;03m# The reason we repeat the same comment below is that\u001b[39;00m\n\u001b[32m    351\u001b[39m \u001b[38;5;66;03m# some Python versions print out the first line of a multi-line function\u001b[39;00m\n\u001b[32m    352\u001b[39m \u001b[38;5;66;03m# calls in the traceback and some print out the last line\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m353\u001b[39m \u001b[43m_engine_run_backward\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m    354\u001b[39m \u001b[43m    \u001b[49m\u001b[43mtensors\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    355\u001b[39m \u001b[43m    \u001b[49m\u001b[43mgrad_tensors_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    356\u001b[39m \u001b[43m    \u001b[49m\u001b[43mretain_graph\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    357\u001b[39m \u001b[43m    \u001b[49m\u001b[43mcreate_graph\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    358\u001b[39m \u001b[43m    \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m    359\u001b[39m \u001b[43m    \u001b[49m\u001b[43mallow_unreachable\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m    360\u001b[39m \u001b[43m    \u001b[49m\u001b[43maccumulate_grad\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[32m    361\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~/.conda/envs/dl/lib/python3.11/site-packages/torch/autograd/graph.py:824\u001b[39m, in \u001b[36m_engine_run_backward\u001b[39m\u001b[34m(t_outputs, *args, **kwargs)\u001b[39m\n\u001b[32m    822\u001b[39m     unregister_hooks = _register_logging_hooks_on_whole_graph(t_outputs)\n\u001b[32m    823\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m824\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mVariable\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_execution_engine\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun_backward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m  \u001b[49m\u001b[38;5;66;43;03m# Calls into the C++ engine to run the backward pass\u001b[39;49;00m\n\u001b[32m    825\u001b[39m \u001b[43m        \u001b[49m\u001b[43mt_outputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\n\u001b[32m    826\u001b[39m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m  \u001b[38;5;66;03m# Calls into the C++ engine to run the backward pass\u001b[39;00m\n\u001b[32m    827\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[32m    828\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m attach_logging_hooks:\n",
      "\u001b[31mKeyboardInterrupt\u001b[39m: "
     ]
    }
   ],
   "source": [
    "from torch import Tensor\n",
    "\n",
    "def calc_net_loss(net: Forward_Net, test_iter: DataLoader, loss_func: torch.nn.Module):\n",
    "    net.eval()\n",
    "    loss_sum = 0\n",
    "    with torch.no_grad():\n",
    "        for x, y in test_iter:\n",
    "            x = x.view(-1, 28*28)\n",
    "            pred:Tensor = net(x)\n",
    "            loss = loss_func(pred, y)\n",
    "            loss_sum += loss.item()\n",
    "    return loss_sum / len(test_iter)\n",
    "\n",
    "def calc_net_acc(net: Forward_Net, test_iter: DataLoader):\n",
    "    net.eval()\n",
    "    correct_predictions = 0\n",
    "    total_samples = 0\n",
    "    with torch.no_grad():\n",
    "        for x, y in test_iter:\n",
    "            x = x.view(-1, 28*28)\n",
    "            pred:Tensor = net(x)\n",
    "            predicted_labels = pred.argmax(dim=1).long()\n",
    "            correct_predictions += (predicted_labels == y.long()).sum().item()\n",
    "            total_samples += y.size(0)\n",
    "    return correct_predictions / total_samples\n",
    "\n",
    "\n",
    "batch_size = 32\n",
    "lr = 0.01\n",
    "loss_func = torch.nn.CrossEntropyLoss()\n",
    "epoch_num = 30\n",
    "\n",
    "train_iter, test_iter = make_minist_dataset()\n",
    "net = Forward_Net(28*28, 256, 10)\n",
    "optimizer = torch.optim.SGD(net.parameters(), lr=lr)\n",
    "\n",
    "train_loss, test_loss, test_acc = [], [], []\n",
    "for epoch in range(epoch_num):\n",
    "    train_loss_epoch = 0\n",
    "    for x, y in train_iter:\n",
    "        x = x.view(-1, 28*28)\n",
    "        pred = net(x)\n",
    "        # print(x.shape, x.view(-1, 28*28).shape, pred.shape, y.shape)\n",
    "        loss = loss_func(pred, y)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        optimizer.zero_grad()\n",
    "        train_loss_epoch += loss.item()\n",
    "    train_loss.append(train_loss_epoch / len(train_iter))\n",
    "    test_loss.append(calc_net_loss(net, test_iter, loss_func))\n",
    "    test_acc.append(calc_net_acc(net, test_iter))\n",
    "    print(f\"Epoch {epoch+1}, Train Loss: {train_loss[-1]}, Test Loss: {test_loss[-1]}, Test Acc: {test_acc[-1]}\")\n",
    "\n",
    "# 损失曲线\n",
    "plt.plot(range(1, epoch_num+1), train_loss, label='Train Loss')\n",
    "plt.plot(range(1, epoch_num+1), test_loss, label='Test Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Training and Test Loss')\n",
    "plt.legend()\n",
    "plt.show()\n",
    "# 测试准确率\n",
    "plt.plot(range(1, epoch_num+1), test_acc, label='Test Accuracy')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Accuracy')\n",
    "plt.title('Test Accuracy')\n",
    "plt.legend()\n",
    "plt.grid(True)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "实验结果分析：随着epoch增加，训练集和测试集上的loss逐渐降低，准确率逐渐提高，表明模型在解决多分类任务的训练过程中不断优化，并拥有了较好的泛化能力。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "dl",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
